#pragma once

// -----------------------------------------------------------------------------

#include "Win32.IO.TextFile.h"

namespace Kif
{
namespace Win32
{
namespace IO
{

enum AsciiCode
{
   Ascii_NUL = 0x00,
   Ascii_SOH = 0x01,
   Ascii_STX = 0x02,
   Ascii_ETX = 0x03,
   Ascii_EOT = 0x04,
   Ascii_ENQ = 0x05,
   Ascii_ACK = 0x06,
   Ascii_BEL = 0x07,
   Ascii_BS  = 0x08,
   Ascii_HT  = 0x09,
   Ascii_LF  = 0x0a,
   Ascii_VT  = 0x0b,
   Ascii_FF  = 0x0c,
   Ascii_CR  = 0x0d,
   Ascii_SO  = 0x0e,
   Ascii_SI  = 0x0f,

   Ascii_DLE = 0x10,
   Ascii_DC1 = 0x11,
   Ascii_DC2 = 0x12,
   Ascii_DC3 = 0x13,
   Ascii_DC4 = 0x14,
   Ascii_NAK = 0x15,
   Ascii_SYN = 0x16,
   Ascii_ETB = 0x17,
   Ascii_CAN = 0x18,
   Ascii_EM  = 0x19,
   Ascii_SUB = 0x1a,
   Ascii_ESC = 0x1b,
   Ascii_FS  = 0x1c,
   Ascii_GS  = 0x1d,
   Ascii_RS  = 0x1e,
   Ascii_US  = 0x1f,

   Ascii_SP  = 0x20,

   Ascii_DEL = 0x7f
}  ;

/// <summary>request result</summary>
template<typename T>
class TextBuffer
{
private:
   T *ptrCurrentChar;

public:

   DWORD LineMaxLen;
   Kif::Win32::String Line;

   Kif::Win32::Diagnostics::Result LastResult;
   Kif::Win32::String FilePath;
   DWORD  BufferLength;

   // use the following to indicate that the memory resources must be disposed
   // here, by default this will be true.
   bool MustDisposeLocally;

   /// <summary>Setup the current buffer character position. if the given
   /// char addres is not withing the buffer an exception is thrown</summary>
   void SetCurrentBuffer(T *currentChar)
   {
      if ((DWORD)(currentChar - ptrRawBuffer) < BufferLength)
         ptrCurrentChar = currentChar;
      else
         throw _T("TextBuffer::SetCurrentBuffer out of space area");
   }  // end of SetCurrentBuffer

   // the raw buffer (char) is a null terminated string of "BufferLength"+1
   // of length
   T *ptrRawBuffer;

   void FreeBuffer()
   {
      LastResult.Clear();
      FilePath.Dispose();
      BufferLength = 0;

      if (ptrRawBuffer && MustDisposeLocally)
         Kif::Win32::Memory::free(ptrRawBuffer);
      ptrRawBuffer = NULL;
      ptrCurrentChar = NULL;
   }  // end of FreeBuffer

   TextBuffer()
   {
      MustDisposeLocally = true;
      LineMaxLen   = 1024;
      ptrRawBuffer = NULL;
      FreeBuffer();
   }  // end of TextBuffer (ctor)

  ~TextBuffer()
   {
      FreeBuffer();
   }  // end of  TextBuffer (dtor)

   // --------------------------------------------------------------------------

   /// <summary>Get the number of chars already read</summary>
   /// <returns>returns the number of read chars</returns>
   inline __w64 int GetNumberOfReadChars()
   {
      return(ptrCurrentChar - ptrRawBuffer);
   }  // end of GetNumberOfReadChars

   /// <summary>Have we read all available chars</summary>
   /// <returns>true is returned if we are at the end of buffer</returns>
   inline bool IsEndOfBuffer()
   {
      return(GetNumberOfReadChars() >= (__w64 int)BufferLength);
   }  // end of IsEndOfBuffer

   /// <summary>Have we read all available chars</summary>
   /// <returns>true is returned if we are at the end of buffer</returns>
   inline __w64 int RemainingBufferLength()
   {
      return(GetNumberOfReadChars() >= (__w64 int)BufferLength);
   }  // end of IsEndOfBuffer

   /// <summary>Read next char</summary>
   /// <returns>a pointer to the current evaluated char is returned</returns>
   T TextBuffer::ReadChar()
   {
      if (!IsEndOfBuffer())
      {
         T nextChar = *ptrCurrentChar;
         ptrCurrentChar++;
         return(nextChar);
      }
      else
         return((T)Ascii_NUL);
   }  // end of ReadChar

   // --------------------------------------------------------------------------

   /// <summary>Read a line until a non-printable char is found</summary>
   /// <returns>a pointer to the current evaluated char is returned</returns>
   T *TextBuffer::ReadLine()
   {
      // if there is no more input data, just return NULL
      if (IsEndOfBuffer())
         return(NULL);

      if (ptrCurrentChar == NULL)
         return(NULL);
      if (*ptrCurrentChar == NULL)
         return(NULL);

      // skip non-printable chars and eat training blanks
      while (*ptrCurrentChar && (*ptrCurrentChar < ' '))
         ptrCurrentChar++;

      // loop through all input until a null char or non-printable char is found
      __w64 int rchrs = GetNumberOfReadChars();
      TCHAR *pBuffer = Line.GetBuffer();
      Line.length = 0;

      unsigned char space = ' ';
      unsigned char currChar = (unsigned char)*ptrCurrentChar;

      while (*ptrCurrentChar && (currChar >= space))
      {
         *pBuffer = *ptrCurrentChar;
         pBuffer++;
         ptrCurrentChar++;
         currChar = (unsigned char)*ptrCurrentChar;

         Line.length++;
         rchrs++;

         if (Line.length >= (__w64 int)LineMaxLen)  // no more output space?
            break;
         else
         if (rchrs >= (__w64 int)BufferLength)      // EOB? (no more input available?)
            break;
      }
      *pBuffer = NULL;

      return(ptrCurrentChar);
   }  // end of ReadLine

   /// <summary>Return a pointer to the last found char</summary>
   /// <returns>Returns a pointer to the last found char</returns>
   inline T *GetCurrentChar()
   {
      return(ptrCurrentChar);
   }  // end of GetCurrentChar

   /// <summary>Return the number of chars remaining</summary>
   /// <returns>number of remaining chars to read is returned</returns>
   inline size_t GetRemainingBufferLength()
   {
      return(BufferLength - GetNumberOfReadChars()); 
   }  // end of GetRemainingBufferLength

   // --------------------------------------------------------------------------

   /// <summary>Move to the top / first char of the buffer</summary>
   /// <returns>returns a pointer to the top char</returns>
   T *MoveToFirstChar()
   {
      // make sure that we do have a line buffer to stuff result into
      if (Line.GetRawBuffer() == NULL)
         Line.AllocBuffer(LineMaxLen+1);

      // reposition current char
      ptrCurrentChar = ptrRawBuffer;
      return(ptrCurrentChar);
   }  // end of MoveToFirstChar

   /// <summary>Set raw buffer and prepare to read first line</summary>
   /// <param name="buffer">buffer to init the raw-buffer</param>
   /// <param name="bufferLength">buffer length</param>
   void SetRawBuffer(T *buffer, DWORD bufferLength)
   {
      ptrRawBuffer = buffer;
      BufferLength = bufferLength;
      MoveToFirstChar();
   }  // end of SetRawBuffer

}  ;  // end of TextBuffer

}  // end of Kif::Win32::IO
}  // end of Kif::Win32
}  // end of Kif

